package org.hilo.boot.core.shiro;

import java.io.Serializable;
import java.util.Collection;
import java.util.Date;

import org.apache.shiro.session.Session;
import org.hilo.boot.core.UT;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 登录一台服务器，就可以任意切换到其他服务器 （但是注意，不要在用户登录时初始化非session数据，
 * 否则切换到其他服务器时，由于没有执行登录方法，数据得不到初始化。
 *  如果真的有这个需要，可以把数据放到session中，这样就能跨服务器共享）
 *  <br>
 *  关于集群环境下的Session设计说明，参见我的博客: http://blog.zollty.com
 * 
 * @author zollty
 * @since 2017-06-30
 */
public class CacheSessionDao extends AbstractCacheSessionDao {
    private static final Logger logger = LoggerFactory.getLogger(CacheSessionDao.class);

    private SessionCacheService sessionCacheService;

    // 正常情况下，该方法不会被调用，只有当本地缓存中找不到session了，或者设置了多少时间强制更新session，才会读取远程session
    @Override
    protected Session doReadSession(Serializable sessionId) {
        logger.debug("begin doReadSession {} ", sessionId);
        Session session = null;
        try {
            session = sessionCacheService.getSession(sessionId);
            if (session != null) {
                // 重置Redis中缓存过期时间
                sessionCacheService.refreshSession(session);
                logger.debug("sessionId {} 被读取.", sessionId);
            }
        } catch (Exception e) {
            logger.warn("读取Session失败: " + e.getMessage());
        }
        return session;
    }

    /**
     * 如DefaultSessionManager在创建完session后会调用该方法； 如保存到关系数据库/文件系统/NoSQL数据库；即可以实现会话的持久化；
     */
    @Override
    protected Serializable doCreate(Session session) {
        // 创建一个Id并设置给Session
        Serializable sessionId = this.generateSessionId(session);
        assignSessionId(session, sessionId);
        try {
            sessionCacheService.saveSession(session); // 这个要设置自动过期时间
            ExtSimpleSession ses = (ExtSimpleSession) session;
            ses.setLastUpdateTime(System.currentTimeMillis());
            logger.info("sessionId {} 被创建. {}", sessionId, UT.Json.toJSONString(session));
        } catch (Exception e) {
            logger.warn("创建Session失败.", e);
        }
        return sessionId;
    }

    /**
     * 更新会话；如更新会话最后访问时间/停止会话/设置超时时间/设置移除属性等会调用
     */
    @Override
    protected void doUpdate(Session session) {
        try {
            // 没有登录时，不需要将session保存到集中缓存
            if (!ShiroSessionFactory.isLogged(session)) {
                return;
            }
            ExtSimpleSession ses = (ExtSimpleSession) session;
            
            if (ses.reachUpdateTime() || ses.isChanged()) {
                // 到了更新时间  或者 有字段(除lastAccessTime外)发生改变
                
                sessionCacheService.updateSession(session);
                ses.setChanged(false); // 恢复成 unchanged的状态，以便下次使用
                // 更新之后，将LastUpdateTime值变为最新
                ses.setLastUpdateTime(System.currentTimeMillis());

                logger.debug("sessionId {} 被更新. {}", session.getId(), UT.Json.toJSONString(session));
            }
            
            // 无条件更新 LastAccessTime
            ses.setLastAccessTime(new Date());
            
        } catch (Exception e) {
            logger.warn("更新Session失败", e);
        }
    }

    @Override
    protected void doDelete(Session session) {
        logger.debug("begin doDelete {} ", session);
        try {
            sessionCacheService.deleteSession(session.getId());
            logger.debug("shiro session id {} 被删除", session.getId());
        } catch (Exception e) {
            logger.warn("删除Session失败", e);
        }
    }

    @Override
    public Collection<Session> getActiveSessions() {

        return getCacheManagerActiveSessions();
    }

    public void setSessionCacheService(SessionCacheService sessionCacheService) {
        this.sessionCacheService = sessionCacheService;
    }
}
